package mskruch.gps.data;

import gpstools.data.UnsupportedFormatException;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import mskruch.gps.Lap;
import mskruch.gps.Point;
import mskruch.gps.PointFactory;
import mskruch.gps.Track;

import com.garmin.fit.Decode;
import com.garmin.fit.LapMesg;
import com.garmin.fit.LapMesgListener;
import com.garmin.fit.MesgBroadcaster;
import com.garmin.fit.RecordMesg;
import com.garmin.fit.RecordMesgListener;
import com.garmin.fit.SessionMesg;
import com.garmin.fit.SessionMesgListener;

/**
 * 
 * @author spr
 *
 */
@Deprecated
public class FitLoader implements TrackLoaderAPI {

	private final static double SEMICIRCLE_FACTOR = (double) (1L << 31) / 180.0d;

	private static double convertSemicirclesToDegrees(int semicircle) {
		return ((double) semicircle / SEMICIRCLE_FACTOR);
	}

	private static Point processRecord(RecordMesg recordMesg) {

		try {
			final double lon = convertSemicirclesToDegrees(recordMesg.getPositionLong());
			final double lat = convertSemicirclesToDegrees(recordMesg.getPositionLat());
			final double ele = recordMesg.getAltitude();
			final long time = recordMesg.getTimestamp().getDate().getTime();

			Short hr = null;
			if (recordMesg.getHeartRate() != null) {
				hr = recordMesg.getHeartRate();
			}
			Short cad = null;
			if (recordMesg.getCadence() != null) {
				cad = recordMesg.getCadence();
			}

			return PointFactory.newPoint(lon, lat, ele, time, hr, cad);
		} catch (Exception e) {
			return null;
		}
	}

	private int index = 0;

	List<Lap> laps = new ArrayList<Lap>();

	List<Point> points = new ArrayList<Point>();

	private List<Track> tracks = new ArrayList<Track>();

	public FitLoader(byte[] data) throws UnsupportedFormatException {
		this(new ByteArrayInputStream(data));
	}

	public FitLoader(InputStream inputStream) throws UnsupportedFormatException {

		MesgBroadcaster broadcaster = new MesgBroadcaster(new Decode());
		broadcaster.addListener(new RecordMesgListener() {

			@Override
			public void onMesg(RecordMesg recordMesg) {
				addPoint(recordMesg);
			}
		});
		broadcaster.addListener(new LapMesgListener() {

			@Override
			public void onMesg(LapMesg lapMesg) {
				newLap(lapMesg);
			}
		});
		
		broadcaster.addListener(new SessionMesgListener() {
			
			@Override
			public void onMesg(SessionMesg sessionMsg) {
				
				closeLap(null);
				if (laps.size()>0) {
					tracks.add(new Track("", laps.toArray(new Lap[laps.size()]), sessionMsg.getTotalDistance(),
							sessionMsg.getTotalAscent (),
							sessionMsg.getTotalDescent (),
							sessionMsg.getAvgCadence(),
							sessionMsg.getAvgHeartRate(),
							sessionMsg.getAvgSpeed(),
							sessionMsg.getTotalElapsedTime(),
							sessionMsg.getTotalTimerTime()));
				}
			}
		});
		broadcaster.run(inputStream);
	}

	public FitLoader(String fileName) throws FileNotFoundException, UnsupportedFormatException {
		this(new FileInputStream(fileName));
	}

	private void addPoint(RecordMesg recordMesg) {
		Point point = processRecord(recordMesg);
		if (point != null) {
			points.add(point);
		}
	}

	private void closeLap(LapMesg lapMesg) {
		if (points != null && !points.isEmpty()) {
			Lap lap = null;
			if (lapMesg==null) {
				lap = new Lap(points.toArray(new Point[points.size()]));
			} else {
			    lap = new Lap(points.toArray(new Point[points.size()]), 
					lapMesg.getTotalDistance(),
					lapMesg.getTotalAscent (),
					lapMesg.getTotalDescent (),
					lapMesg.getAvgCadence(),
					lapMesg.getAvgHeartRate(),
					lapMesg.getAvgSpeed(),
					lapMesg.getTotalElapsedTime(),
					lapMesg.getTotalTimerTime());
			}
			laps.add(lap);
		}
	}

	public Track[] getTracks() {
		return tracks.toArray(new Track[tracks.size()]);
	}

	private void newLap(LapMesg lapMesg) {
		closeLap(lapMesg);
		points = new ArrayList<Point>();
	}

	public Track nextTrack() {
		if (index < tracks.size()) {
			return tracks.get(index++);
		} else {
			return null;
		}
	}
}
